package com.beardedhen.harmonybootstrap;

import com.beardedhen.harmonybootstrap.api.attributes.BootstrapBrand;
import com.beardedhen.harmonybootstrap.api.defaults.DefaultBootstrapBrand;
import com.beardedhen.harmonybootstrap.utils.ColorUtils;
import com.beardedhen.harmonybootstrap.utils.DimenUtils;
import com.beardedhen.harmonybootstrap.utils.ViewUtils;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.*;
import ohos.agp.components.element.Element;
import ohos.agp.utils.Color;
import ohos.agp.utils.LayoutAlignment;
import ohos.app.Context;
import ohos.global.resource.NotExistException;
import ohos.global.resource.WrongTypeException;
import ohos.miscservices.pasteboard.PasteData;

import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * See
 * <a href="http://getbootstrap.com/components/#badges">http://getbootstrap.com/components/#alerts</a>
 */

public class BootstrapAlert extends DependentLayout implements Animator.StateChangedListener {

    private static final String TAG = "BootstrapAlert";

    /**
     * Provides methods which receive callbacks in response to changes in the view visibility.
     */
    public interface VisibilityChangeListener {

        /**
         * Called when the alert is set to be dismissed with an animation.
         *
         * @param alert the alert
         */
        void onAlertDismissStarted(BootstrapAlert alert);

        /**
         * Called when the alert is no longer visible.
         *
         * @param alert the alert
         */
        void onAlertDismissCompletion(BootstrapAlert alert);

        /**
         * Called when the alert set to appear with an animation
         *
         * @param alert the alert
         */
        void onAlertAppearStarted(BootstrapAlert alert);

        /**
         * Called when the alert is now visible.
         *
         * @param alert the alert
         */
        void onAlertAppearCompletion(BootstrapAlert alert);
    }

    private Image closeButton;

    private BootstrapBrand bootstrapBrand;

    private String strongText;
    private String messageText;

    private float baselineFontSize;
    private float baselinePadding;

    private AnimatorValue fadeInAnimation;
    private AnimatorValue fadeOutAnimation;

    private boolean userDismissible;

    private VisibilityChangeListener visibilityChangeListener;

    private static final AtomicInteger nextGeneratedId = new AtomicInteger(1);

    private static final String BOOTSTRAP_ALERT_BOOTSTRAP_BRAND = "bootstrapBrand";
    private static final String BOOTSTRAP_ALERT_STRONG_TEXT = "strongText";
    private static final String BOOTSTRAP_ALERT_MESSAGE_TEXT = "messageText";
    private static final String BOOTSTRAP_ALERT_DISMISSIBLE = "dismissible";

    public BootstrapAlert(Context context) throws NotExistException, WrongTypeException, IOException {
        super(context);
        initialise(null);
    }

    public BootstrapAlert(Context context, AttrSet attrs) throws NotExistException, WrongTypeException, IOException {
        super(context, attrs);
        initialise(attrs);
    }

    public BootstrapAlert(Context context, AttrSet attrs, String defStyleAttr) throws NotExistException, WrongTypeException, IOException {
        super(context, attrs, defStyleAttr);
        initialise(attrs);
    }

    private int getTypeOrdinal(String brand) {
        int typeOrdinal = 7;

        if (brand.equals("primary")) {
            typeOrdinal = 0;
        } else if (brand.equals("success")) {
            typeOrdinal = 1;
        } else if (brand.equals("info")) {
            typeOrdinal = 2;
        } else if (brand.equals("warning")) {
            typeOrdinal = 3;
        } else if (brand.equals("danger")) {
            typeOrdinal = 4;
        } else if (brand.equals("regular")) {
            typeOrdinal = 5;
        } else if (brand.equals("secondary")) {
            typeOrdinal = 6;
        }
        return typeOrdinal;

    }

    private void initialise(AttrSet attrs) throws NotExistException, WrongTypeException, IOException {
        if (attrs == null) {
            return;
        }
        try {
            int typeOrdinal = attrs.getAttr(BOOTSTRAP_ALERT_BOOTSTRAP_BRAND).isPresent() ?
                getTypeOrdinal(attrs.getAttr(BOOTSTRAP_ALERT_BOOTSTRAP_BRAND).get().getStringValue()) : -1;

            this.bootstrapBrand = DefaultBootstrapBrand.fromAttributeValue(typeOrdinal);

            strongText = attrs.getAttr(BOOTSTRAP_ALERT_STRONG_TEXT).isPresent() ?
                    attrs.getAttr(BOOTSTRAP_ALERT_STRONG_TEXT).get().getStringValue() : "false";
            messageText = attrs.getAttr(BOOTSTRAP_ALERT_MESSAGE_TEXT).isPresent() ?
                    attrs.getAttr(BOOTSTRAP_ALERT_MESSAGE_TEXT).get().getStringValue() : "false";
            userDismissible = attrs.getAttr(BOOTSTRAP_ALERT_DISMISSIBLE).isPresent() ?
                    attrs.getAttr(BOOTSTRAP_ALERT_DISMISSIBLE).get().getBoolValue() : false;

            if (strongText == null) {
                strongText = "";
            }
            if (messageText == null) {
                messageText = "";
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        baselineFontSize = DimenUtils.pixelsFromSpResource(getContext(),
                ResourceTable.Float_bootstrap_button_default_font_size);

        try {
            baselinePadding = DimenUtils.pixelsFromDpResource(getContext(),
                    ResourceTable.Float_bootstrap_alert_paddings);
        } catch (Exception e) {
            e.printStackTrace();
        }
        setupAnimations();
        updateBootstrapState();
    }

    private void updateBootstrapState() {

        Text alertText = new Text(getContext());
        closeButton = new Image(getContext());

        LayoutConfig textParams = new LayoutConfig(LayoutConfig.MATCH_PARENT,
                LayoutConfig.MATCH_CONTENT);
        LayoutConfig closeParams = new LayoutConfig(25,25);
        textParams.addRule(LayoutConfig.LEFT_OF, closeButton.getId());
        closeParams.addRule(LayoutConfig.ALIGN_PARENT_RIGHT, LayoutConfig.TRUE);

        alertText.setLayoutConfig(textParams);
        alertText.setTextSize((int) (baselineFontSize ));
        setAlignment(LayoutAlignment.START);
        alertText.setMultipleLine(true);
        alertText.setTextColor(new Color(ColorUtils.resolveColor(bootstrapBrand.getColor(),getContext())));
        alertText.setText(PasteData.Record.createHtmlTextRecord(String.format("<b>%s</b>%s", strongText,
                (strongText.length() > 0 ?
                        " " + messageText :
                        messageText))).convertToText(getContext()).toString());
        closeButton.setLayoutConfig(closeParams);

        Element buttonBg = BootstrapDrawableFactory.bootstrapAlertCloseIcon(
                getContext(), (int) baselineFontSize, (int) baselineFontSize,
                DimenUtils.dpToPixels(getContext(), 6));

        ViewUtils.setBackgroundDrawable(closeButton, buttonBg);

        Element bg = BootstrapDrawableFactory.bootstrapAlert(getContext(), bootstrapBrand);
        ViewUtils.setBackgroundDrawable(this, bg);
        this.addComponent(alertText);
        if (userDismissible) {
            this.addComponent(closeButton);
            closeButton.setClickedListener(new ClickedListener() {
                @Override
                public void onClick(Component component) {
                    dismiss(true);
                }
            });
        }

        int vert = (int) (baselinePadding * 1.5);
        int hori = (int) (baselinePadding * 1.5);
        setPadding(hori, vert, hori, vert);

    }

    private void setupAnimations() {
        fadeInAnimation = new AnimatorValue();
        fadeInAnimation.setDuration(300);
        fadeInAnimation.setCurveType(Animator.CurveType.ACCELERATE);
        fadeInAnimation.setStateChangedListener(this);

        fadeOutAnimation = new AnimatorValue();
        fadeOutAnimation.setDuration(300);
        fadeOutAnimation.setCurveType(Animator.CurveType.ACCELERATE);
        fadeOutAnimation.setStateChangedListener(this);
    }

    /**
     * Hides the alert with an animation, setting its visibility to {@link Component#HIDE}
     *
     * @param animated whether the dismissal should be animated or not
     */
    public void dismiss(boolean animated) {
        if (animated) {
            if (visibilityChangeListener != null) {
                visibilityChangeListener.onAlertDismissStarted(this);
            }
            fadeOutAnimation.start();
        } else {
            setVisibility(Component.HIDE);
        }
    }

    /**
     * Shows the alert with an animation, setting its visibility to {@link Component#VISIBLE}
     *
     * @param animated whether the appearance should be animated or not
     */
    public void show(boolean animated) {
        if (animated) {
            if (visibilityChangeListener != null) {
                visibilityChangeListener.onAlertAppearStarted(this);
            }
            fadeInAnimation.start();
        } else {
            setVisibility(VISIBLE);
        }
    }

    /**
     * Retrieves whether the user can dismiss the dialog or not
     *
     * @return true if dismissable
     */
    public boolean isUserDismissible() {
        return userDismissible;
    }

    /**
     * Sets whether the user can dismiss the dialog or not.
     *
     * @param userDismissible true if dismissable
     */
    public void setUserDismissible(boolean userDismissible) {
        this.userDismissible = userDismissible;
        updateBootstrapState();
    }

    /**
     * Sets a {@link VisibilityChangeListener} that will be notified on changes
     *
     * @param visibilityChangeListener the listener
     */
    public void setVisibilityChangeListener(VisibilityChangeListener visibilityChangeListener) {
        this.visibilityChangeListener = visibilityChangeListener;
    }

    @Override
    public void setVisibility(int visibility) {
        super.setVisibility(visibility);

        if (visibilityChangeListener != null) {
            if (Component.HIDE == visibility) {
                visibilityChangeListener.onAlertDismissCompletion(this);
            } else if (VISIBLE == visibility) {
                visibilityChangeListener.onAlertAppearCompletion(this);
            }
        }
    }

    // Animation change listener

    @Override
    public void onStart(Animator animation) {

    }

    @Override
    public void onStop(Animator animator) {

    }

    @Override
    public void onCancel(Animator animator) {

    }

    @Override
    public void onEnd(Animator animation) {

        if (animation == fadeInAnimation) {
            setVisibility(Component.VISIBLE);
        } else if (animation == fadeOutAnimation) {
            setVisibility(Component.HIDE);
        } else {
            throw new IllegalStateException("Unsupported animation attempted to use this listener");
        }
    }

    @Override
    public void onPause(Animator animator) {

    }

    @Override
    public void onResume(Animator animator) {

    }
}
